/*
 * Copyright 2000 the original author or authors.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "WtTableUtil.h"
#include "WtTable.h"
#include "WtUtil.h"

/* Convert a table to a list */

Tcl_Obj *WtTableToList(Tcl_Obj *table, int takeFirstValue, Tcl_Interp *interp)
{
  int ok = 1;
  Tcl_HashTable *hashTable = WtGetTableMap(table);
  Tcl_HashEntry *ent;
  Tcl_HashSearch search;
  Tcl_Obj *list, *key, *val, *first;

  list = Tcl_NewListObj(0, NULL);
  ent = Tcl_FirstHashEntry(hashTable, &search);

  while (ent) {    
    key = (Tcl_Obj *)Tcl_GetHashKey(hashTable, ent);
    if (Tcl_ListObjAppendElement(interp, list, key) != TCL_OK) {
      ok = 0;
      break;
    } else {
      val = (Tcl_Obj *)Tcl_GetHashValue(ent);
      if (takeFirstValue) {
        if (Tcl_ListObjIndex(NULL, val, 0, &first) == TCL_OK) {
          val = first;
        } else {
          ok = 0;
          break;
        }
      }
      if (Tcl_ListObjAppendElement(interp, list, val) != TCL_OK) {
        ok = 0;
        break;
      }
    }
    ent = Tcl_NextHashEntry(&search);
  }

  if (!ok) {
    Tcl_DecrRefCount(list);
    list = NULL;
  }

  return list;
}

/* Convert a table to a Tcl array */

int WtTableToArray(Tcl_Interp *interp, Tcl_Obj *table,
    Tcl_Obj *arrName, int takeFirstValue)
{
  int ok = 1;
  Tcl_HashTable *hashTable = WtGetTableMap(table);
  Tcl_HashEntry *ent;
  Tcl_HashSearch search;
  Tcl_Obj *key, *val, *first;

  ent = Tcl_FirstHashEntry(hashTable, &search);

  while (ent) {    
    key = (Tcl_Obj *)Tcl_GetHashKey(hashTable, ent);
    val = (Tcl_Obj *)Tcl_GetHashValue(ent);
    if (takeFirstValue) {
      if (Tcl_ListObjIndex(NULL, val, 0, &first) == TCL_OK) {
        val = first;
      } else {
        ok = 0;
        break;
      }
    }
    if (!Tcl_ObjSetVar2(interp, arrName, key, val, TCL_LEAVE_ERR_MSG)) {
      ok = 0;
      break;
    }
    ent = Tcl_NextHashEntry(&search);
  }

  return ok;
}

int WtTableHasStr(Tcl_Obj *tbl, const char *key)
{
  Tcl_Obj *keyObj, *valObj;
  keyObj = WtNewString(key);
  Tcl_IncrRefCount(keyObj);
  valObj = WtTableGet(tbl, keyObj);
  Tcl_DecrRefCount(keyObj);
  return valObj != NULL;
}

void WtTableSetStrToStr(Tcl_Obj *tbl, const char *key, const char *val)
{
  Tcl_Obj *keyObj, *valObj;

  keyObj = WtNewString(key);
  Tcl_IncrRefCount(keyObj);
  valObj = WtNewString(val);
  Tcl_IncrRefCount(valObj);

  WtTableSet(tbl, keyObj, valObj);

  Tcl_DecrRefCount(keyObj);
  Tcl_DecrRefCount(valObj);
}

void WtTableSetStrToInt(Tcl_Obj *tbl, const char *key, int val)
{
  Tcl_Obj *keyObj, *valObj;

  keyObj = WtNewString(key);
  Tcl_IncrRefCount(keyObj);

  valObj = Tcl_NewIntObj(val);
  Tcl_IncrRefCount(valObj);

  WtTableSet(tbl, keyObj, valObj);

  Tcl_DecrRefCount(keyObj);
  Tcl_DecrRefCount(valObj);
}

void WtTableSetStrToBool(Tcl_Obj *tbl, const char *key, int val)
{
  Tcl_Obj *keyObj, *valObj;

  keyObj = WtNewString(key);
  Tcl_IncrRefCount(keyObj);

  valObj = Tcl_NewBooleanObj(val);
  Tcl_IncrRefCount(valObj);

  WtTableSet(tbl, keyObj, valObj);

  Tcl_DecrRefCount(keyObj);
  Tcl_DecrRefCount(valObj);
}

void WtTableSetStrToLong(Tcl_Obj *tbl, const char *key, long val)
{
  Tcl_Obj *keyObj, *valObj;

  keyObj = WtNewString(key);
  Tcl_IncrRefCount(keyObj);

  valObj = Tcl_NewLongObj(val);
  Tcl_IncrRefCount(valObj);

  WtTableSet(tbl, keyObj, valObj);

  Tcl_DecrRefCount(keyObj);
  Tcl_DecrRefCount(valObj);
}

void WtTableSetStrToDouble(Tcl_Obj *tbl, const char *key, double val)
{
  Tcl_Obj *keyObj, *valObj;

  keyObj = WtNewString(key);
  Tcl_IncrRefCount(keyObj);

  valObj = Tcl_NewDoubleObj(val);
  Tcl_IncrRefCount(valObj);

  WtTableSet(tbl, keyObj, valObj);

  Tcl_DecrRefCount(keyObj);
  Tcl_DecrRefCount(valObj);
}

void WtTableSetStrToObj(Tcl_Obj *tbl, const char *key, Tcl_Obj *val)
{
  Tcl_Obj *keyObj;

  keyObj = WtNewString(key);
  Tcl_IncrRefCount(keyObj);

  WtTableSet(tbl, keyObj, val);

  Tcl_DecrRefCount(keyObj);
}

void WtTableSetoIntToObj(Tcl_Obj *tbl, int key, Tcl_Obj *val)
{
  Tcl_Obj *keyObj;

  keyObj = Tcl_NewIntObj(key);
  Tcl_IncrRefCount(keyObj);

  WtTableSet(tbl, keyObj, val);

  Tcl_DecrRefCount(keyObj);
}

int WtTableAppendStrToList(Tcl_Obj *tbl, const char *key, const char *val,
    Tcl_Interp *interp)
{
  int ok;
  Tcl_Obj *keyObj, *valObj;

  keyObj = WtNewString(key);
  Tcl_IncrRefCount(keyObj);
  valObj = WtNewString(val);
  Tcl_IncrRefCount(valObj);

  ok = WtTableAppendToList(tbl, keyObj, valObj, NULL, interp);

  Tcl_DecrRefCount(keyObj);
  Tcl_DecrRefCount(valObj);

  return ok;
}

int WtTableAppendObjToList(Tcl_Obj *tbl, const char *key, Tcl_Obj *valObj,
    Tcl_Interp *interp)
{
  int ok;
  Tcl_Obj *keyObj;

  keyObj = WtNewString(key);
  Tcl_IncrRefCount(keyObj);
  ok = WtTableAppendToList(tbl, keyObj, valObj, NULL, interp);
  Tcl_DecrRefCount(keyObj);

  return ok;
}

char *WtTableGetStrFromStr(Tcl_Obj *tbl, const char *key, char *fallback)
{
  Tcl_Obj *keyObj, *valObj;

  keyObj = WtNewString(key);
  Tcl_IncrRefCount(keyObj);

  valObj = WtTableGet(tbl, keyObj);

  Tcl_DecrRefCount(keyObj);

  return valObj ? WtToString(valObj) : fallback;
}

int WtTableGetIntFromStr(Tcl_Obj *tbl, const char *key, int fallback)
{
  int ret = fallback, intVal;
  Tcl_Obj *keyObj, *valObj;

  keyObj = WtNewString(key);
  Tcl_IncrRefCount(keyObj);

  valObj = WtTableGet(tbl, keyObj);

  Tcl_DecrRefCount(keyObj);

  if (valObj) {
    if (Tcl_GetIntFromObj(NULL, valObj, &intVal) == TCL_OK) {
      ret = intVal;
    }
  }

  return ret;
}

int WtTableGetBoolFromStr(Tcl_Obj *tbl, const char *key, int fallback)
{
  int ret = fallback, intVal;
  Tcl_Obj *keyObj, *valObj;

  keyObj = WtNewString(key);
  Tcl_IncrRefCount(keyObj);

  valObj = WtTableGet(tbl, keyObj);

  Tcl_DecrRefCount(keyObj);

  if (valObj) {
    if (Tcl_GetBooleanFromObj(NULL, valObj, &intVal) == TCL_OK) {
      ret = intVal;
    }
  }

  return ret;
}

Tcl_Obj *WtTableGetObjFromStr(Tcl_Obj *tbl, const char *key, Tcl_Obj *fallback)
{
  Tcl_Obj *keyObj, *valObj;

  keyObj = WtNewString(key);
  Tcl_IncrRefCount(keyObj);

  valObj = WtTableGet(tbl, keyObj);

  Tcl_DecrRefCount(keyObj);

  return valObj ? valObj : fallback;
}

Tcl_Obj *WtTableGetObjFromInt(Tcl_Obj *tbl, int key, Tcl_Obj *fallback)
{
  Tcl_Obj *keyObj, *valObj;

  keyObj = Tcl_NewIntObj(key);
  Tcl_IncrRefCount(keyObj);

  valObj = WtTableGet(tbl, keyObj);

  Tcl_DecrRefCount(keyObj);

  return valObj ? valObj : fallback;
}

int WtTableGetListFromStr(Tcl_Obj *tbl, const char *keyStr, Tcl_Obj **val,
    Tcl_Obj *fallback, Tcl_Interp *interp)
{
  int ret = 0;
  Tcl_Obj *key;

  key = WtNewString(keyStr);
  Tcl_IncrRefCount(key);

  ret = WtTableGetListDefault(tbl, key, val, fallback, interp);

  Tcl_DecrRefCount(key);

  return ret;
}

void WtTableRemoveStr(Tcl_Obj *tbl, const char *key)
{
  Tcl_Obj *keyObj;

  keyObj = WtNewString(key);
  Tcl_IncrRefCount(keyObj);

  WtTableRemove(tbl, keyObj);

  Tcl_DecrRefCount(keyObj);
}

int WtOwnTable(Tcl_Obj **objPtr, Tcl_Interp *interp)
{
  WtCopyOnWrite(objPtr);
  return WtConvertToTable(*objPtr, interp);
}

int WtOwnCaselessTable(Tcl_Obj **objPtr, Tcl_Interp *interp)
{
  WtCopyOnWrite(objPtr);
  return WtConvertToCaselessTable(*objPtr, interp);
}

int WtOwnTableObj(Tcl_Obj **objPtr, Tcl_ObjType *type, Tcl_Interp *interp)
{
  WtCopyOnWrite(objPtr);
  return WtConvertToTableObj(*objPtr, type, interp);
}

int WtImportTableVars(Tcl_Obj *tbl, Tcl_Interp *interp)
{
  int ok = 1;
  Tcl_HashTable *hashTable = WtGetTableMap(tbl);
  Tcl_HashEntry *ent;
  Tcl_HashSearch search;
  Tcl_Obj *key, *val;

  ent = Tcl_FirstHashEntry(hashTable, &search);

  while (ent) {    
    key = (Tcl_Obj *)Tcl_GetHashKey(hashTable, ent);
    val = (Tcl_Obj *)Tcl_GetHashValue(ent);
    if (!Tcl_ObjSetVar2(interp, key, NULL, val,
        TCL_LEAVE_ERR_MSG)) {
      ok = 0;
      break;
    }
    ent = Tcl_NextHashEntry(&search);
  }

  return ok;
}
